package cn.wei.youvierpc.spi;

import cn.hutool.core.io.resource.ResourceUtil;
import cn.wei.youvierpc.serializer.Serializer;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class SpiLoader {

    private static Map<String,Map<String, Class<?>>> loadedMap = new ConcurrentHashMap<>();

    private static Map<String, Object> instanceCache = new ConcurrentHashMap<>();

    private static final String RPC_SYSTEM_SPI_DIR = "META-INF/rpc/system/";

    private static final String RPC_CUSTOM_SPI_DIR = "META-INF/rpc/custom/";

    private static String[] SCAN_DIRS = new String[]{RPC_CUSTOM_SPI_DIR, RPC_SYSTEM_SPI_DIR};

    private static final List<Class<?>> LOAD_CLASS_LIST = Arrays.asList(Serializer.class);

    public static void loadAll() {
        log.info("加载所有的spi");

        for (Class<?> aClass : LOAD_CLASS_LIST) {
            load(aClass);
        }
    }

    public static Map<String, Class<?>> load(Class<?> aClass) {
        log.info("加载SPI类型：{}", aClass.getName());

        Map<String, Class<?>> classMap = new HashMap<>();

        for (String scanDir : SCAN_DIRS) {
            List<URL> resources = ResourceUtil.getResources(scanDir + aClass.getName());
            for (URL resource : resources) {
                try {
                    InputStreamReader inputStreamReader = new InputStreamReader(resource.openStream());
                    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
                    String line;
                    while ((line = bufferedReader.readLine()) != null) {
                        String[] strArr = line.split("=");
                        if (strArr.length > 0) {
                            String key = strArr[0];
                            String value = strArr[1];
                            classMap.put(key, Class.forName(value));
                        }
                    }
                } catch (Exception e) {
                    log.error("spi resource load error", e);
                }
            }

        }
        loadedMap.put(aClass.getName(), classMap);
        return classMap;
    }

    public static <T> T getInstance(Class<?> tClass, String key) {
        String className = tClass.getName();
        Map<String, Class<?>> classMap = loadedMap.get(className);
        if (classMap == null) {
            throw new RuntimeException(String.format("SpiLoader 未加载%s类型", className));
        }
        if (!classMap.containsKey(key)) {
            throw new RuntimeException(String.format("SpiLoader 的%s下不存在%s类型", className, key));
        }
        Class<?> implClass = classMap.get(key);

        String implClassName = implClass.getName();
        if (!instanceCache.containsKey(implClassName)) {
            try {
                instanceCache.put(implClassName, implClass.getDeclaredConstructor().newInstance());
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                     NoSuchMethodException e) {
                throw new RuntimeException(String.format(implClassName + "%s类实例化失败！"));
            }
        }
        return (T) instanceCache.get(implClassName);
    }

}
